App Open
App Open ads are full-screen ads that appear when users launch your app, providing an excellent opportunity to monetize app startup moments. Unlike interstitial ads, App Open ads are specifically designed for app launch scenarios and help create a smooth user experience during cold starts and app foregrounding events.
Key Features
- Launch Optimization: Designed specifically for app startup scenarios
- User-Friendly: Integrates seamlessly with splash screens and loading experiences
- Revenue Maximization: Monetizes high-engagement moments when users actively open your app
- Retention-Focused: Built with user retention best practices in mind
Best Practices
- Always use a splash/loading screen before showing the App Open ad
- Inform users about the upcoming ad with a message like "Watch an ad from our sponsor while the app loads"
- Ensure proper sequence: Splash screen → App Open ad → App content
- Initialize the SDK before loading App Open ads
To maintain good user experience and retention:
- Implement external frequency capping outside of the SDK
- Consider cooldown periods between App Open ads
- Wait for new users to engage with your app before showing their first App Open ad
- Show ads strategically (e.g., every 2nd or 3rd app launch)
Start loading an ad
Load App Open ads during SDK initialization, ideally in your AppDelegate or SceneDelegate.
- Swift
- Objective-C
import XMediator
XMediatorAds.startWith(appKey: "<your-app-key>") { result in
XMediatorAds.appOpen.load(placementId: "<placement-id>")
}
@import XMediatorObjC;
[X3MXMediatorAds startWithAppKey:@"<your-app-key>"
callback:^(NSError * _Nullable error) {
[X3MXMediatorAds.appOpen loadWithPlacementId:@"<placement-id>"];
}];
Presenting an ad
Calling isReady()
will return if there's an App Open ad available to be presented, regardless of its placement id. If multiple ads with different placement ids were previously loaded, the SDK will try to present the best one available.
- Swift
- Objective-C
if XMediatorAds.appOpen.isReady() {
XMediatorAds.appOpen.present(fromViewController: self, fromAdSpace: "app-open-ad-space")
}
if ([X3MXMediatorAds.appOpen isReady]) {
[X3MXMediatorAds.appOpen presentFromViewController:self fromAdSpace:@"app-open-ad-space"];
}
Presenting an ad with placementId
When the app needs to present an ad for a specific placement id, isReady(withPlacementId:)
and present(withPlacementId:)
can be alternatively used.
- Swift
- Objective-C
if XMediatorAds.appOpen.isReady(withPlacementId: "<placement-id>") {
XMediatorAds.appOpen.present(withPlacementId: "<placement-id>", fromViewController: self, fromAdSpace: "app-open-ad-space")
}
if ([X3MXMediatorAds.appOpen isReadyWithPlacementId:@"<placement-id>"]) {
[X3MXMediatorAds.appOpen presentWithPlacementId:@"<placement-id>" fromViewController:self fromAdSpace:@"app-open-ad-space"];
}
Built-in features
Auto loading
When App Open ads are dismissed or fail to show, the SDK automatically triggers a new load request to ensure the next app launch opportunity is ready.
Auto retry
Failed load attempts are automatically retried with exponential backoff, ensuring optimal fill rates for your app launch monetization.
Additional settings
Register ad callbacks
Every ad callback indicates the placement id of the App Open ad that triggered the event.
- Swift
- Objective-C
// Assign a delegate to handle the ad callbacks
XMediatorAds.appOpen.addDelegate(self)
// [...]
func didLoad(placementId: String, result: LoadResult) {
print("App Open loaded! placementId: \(placementId)")
}
func didPresent(placementId: String) {
print("App Open is being presented! placementId: \(placementId)")
}
func failedToPresent(placementId: String, error: PresentError) {
// If you need to resume your app's flow, make sure to do it here and in the didDismiss callback
print("App Open failed to present. placementId: \(placementId), Reason: \(error.localizedDescription)")
}
func didRecordImpression(placementId: String, data: ImpressionData) {
print("App Open impression, with revenue: \(data.revenue). placementId: \(placementId)")
}
func willDismiss(placementId: String) {
print("App Open will be dismissed! placementId: \(placementId)")
}
func didDismiss(placementId: String) {
// If you need to resume your app's flow, make sure to do it here and in the failedToPresent callback
print("App Open dismissed! placementId: \(placementId)")
}
func didClick(placementId: String) {
print("App Open clicked! placementId: \(placementId)")
}
// Assign a delegate to handle the ad callbacks
[X3MXMediatorAds.appOpen addDelegate:self];
// [...]
- (void)didLoadWithPlacementId:(NSString * _Nonnull)placementId result:(LoadResult * _Nonnull)result {
NSLog(@"App Open loaded! placementId: %@", placementId);
}
- (void)didPresentWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open is being presented! placementId: %@", placementId);
}
- (void)failedToPresentWithPlacementId:(NSString * _Nonnull)placementId error:(NSError * _Nonnull)error {
// If you need to resume your app's flow, make sure to do it here and in the didDismiss callback
NSLog(@"App Open failed to present. placementId: %@. Reason %@", placementId, error);
}
- (void)didRecordImpressionWithPlacementId:(NSString * _Nonnull)placementId data:(ImpressionData * _Nonnull)data {
NSLog(@"App Open impression, with revenue: %f. PlacementId %@", data.revenue, placementId);
}
- (void)willDismissWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open will be dismissed! placementId: %@", placementId);
}
- (void)didDismissWithPlacementId:(NSString * _Nonnull)placementId {
// If you need to resume your app's flow, make sure to do it here and in the failedToPresent callback
NSLog(@"App Open dismissed! placementId: %@", placementId);
}
- (void)didClickWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open clicked! placementId: %@", placementId);
}
App Lifecycle Integration
For optimal App Open ad implementation, integrate with iOS's lifecycle events:
- Swift
- SwiftUI
- Objective-C
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
private var appOpenListener: AppOpenListener?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the SDK
XMediatorAds.startWith(appKey: "your_app_key") { result in
// Load an App Open ad for future use
XMediatorAds.appOpen.load("app_open_placement_id")
}
// Set up listener
appOpenListener = AppOpenListener()
XMediatorAds.appOpen.addListener(appOpenListener!)
// Add notification observers for app lifecycle events
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
return true
}
@objc func applicationWillEnterForeground() {
// App comes to foreground - show App Open ad if ready
if XMediatorAds.appOpen.isReady() {
// Get current view controller and show ad
if let rootViewController = UIApplication.shared.windows.first?.rootViewController {
XMediatorAds.appOpen.present(viewController: rootViewController, adSpace: "app-open-ad-space")
}
}
}
}
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
private var appOpenListener: AppOpenListener?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Initialize SDK
XMediatorAds.startWith(appKey: "your_app_key") { result in
// Load an App Open ad for future use
XMediatorAds.appOpen.load("app_open_placement_id")
}
// Set up listener
appOpenListener = AppOpenListener()
XMediatorAds.appOpen.addListener(appOpenListener!)
}
func sceneWillEnterForeground(_ scene: UIScene) {
// App comes to foreground - show App Open ad if ready
if XMediatorAds.appOpen.isReady() {
if let windowScene = scene as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
XMediatorAds.appOpen.presentFromViewController(rootViewController, fromAdSpace: "app-open-ad-space")
}
}
}
}
#import <UIKit/UIKit.h>
#import <XMediatorAdsSDK/XMediatorAds.h>
@interface AppDelegate () <UIApplicationDelegate>
@property (nonatomic, strong) AppOpenListener *appOpenListener;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize the SDK
[X3MXMediatorAds startWithAppKey:@"your_app_key" completion:^(X3MXMInitResult * _Nullable result) {
// Load an App Open ad for future use
[[X3MXMediatorAds appOpen] loadWithPlacementId:@"app_open_placement_id"];
}];
// Set up listener
self.appOpenListener = [[AppOpenListener alloc] init];
[[X3MXMediatorAds appOpen] addListener:self.appOpenListener];
// Add notification observers for app lifecycle events
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
return YES;
}
- (void)applicationWillEnterForeground:(NSNotification *)notification {
// App comes to foreground - show App Open ad if ready
if ([[X3MXMediatorAds appOpen] isReady]) {
// Get current view controller and show ad
UIViewController *rootViewController = [UIApplication sharedApplication].windows.firstObject.rootViewController;
if (rootViewController) {
[[X3MXMediatorAds appOpen] presentFromViewController:rootViewController fromAdSpace:@"app-open-ad-space"];
}
}
}
@end
Code example
In addition to the example below, you can see a real implementation in our iOS Demo App.
- Swift
- SwiftUI
- Objective-C
AppOpenViewController.swift
import UIKit
import XMediator
class AppOpenViewController: UIViewController {
@IBOutlet weak var splashProgressBar: UIActivityIndicatorView!
@IBOutlet weak var splashMessage: UILabel!
private let appKey = "<your-app-key>"
private let appOpenPlacementId = "<placement-id>"
override func viewDidLoad() {
super.viewDidLoad()
setupSplashUI()
initializeSDKAndLoadAd()
}
private func setupSplashUI() {
// Show user-friendly message about upcoming ad
splashMessage.text = "Watch an ad from our sponsor while the app loads..."
splashProgressBar.startAnimating()
}
private func initializeSDKAndLoadAd() {
XMediatorAds.appOpen.addDelegate(self)
// Initialize SDK and load App Open ad
XMediatorAds.startWith(appKey: appKey) { result in
XMediatorAds.appOpen.load(placementId: self.appOpenPlacementId)
}
// Show ad after a short delay to ensure splash screen is visible
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showAppOpenAdIfReady()
}
}
private func showAppOpenAdIfReady() {
if XMediatorAds.appOpen.isReady(withPlacementId: appOpenPlacementId) {
XMediatorAds.appOpen.present(withPlacementId: appOpenPlacementId, fromViewController: self, fromAdSpace: "app-open-ad-space")
} else {
// No ad ready, proceed to main app
proceedToMainApp()
}
}
private func proceedToMainApp() {
// Hide splash elements and show main app content
splashProgressBar.stopAnimating()
splashProgressBar.isHidden = true
splashMessage.text = "Welcome to the app!"
// Navigate to main view controller or show main content
// performSegue(withIdentifier: "showMainApp", sender: self)
}
}
extension AppOpenViewController: AppOpenAdsDelegate {
func didLoad(placementId: String, result: LoadResult) {
print("App Open loaded! placementId: \(placementId)")
}
func didPresent(placementId: String) {
print("App Open is being presented! placementId: \(placementId)")
// Hide splash screen when ad is shown
splashProgressBar.stopAnimating()
splashProgressBar.isHidden = true
}
func failedToPresent(placementId: String, error: PresentError) {
// If you need to resume your app's flow, make sure to do it here and in the didDismiss callback
print("App Open failed to present. placementId: \(placementId), Reason: \(error.localizedDescription)")
proceedToMainApp()
}
func didRecordImpression(placementId: String, data: ImpressionData) {
print("App Open impression, with revenue: \(data.revenue). placementId: \(placementId)")
}
func willDismiss(placementId: String) {
print("App Open will be dismissed! placementId: \(placementId)")
}
func didDismiss(placementId: String) {
// If you need to resume your app's flow, make sure to do it here and in the failedToPresent callback
print("App Open dismissed! placementId: \(placementId)")
proceedToMainApp()
}
func didClick(placementId: String) {
print("App Open clicked! placementId: \(placementId)")
}
}
AppOpenView.swift
import SwiftUI
import XMediator
// MARK: - SwiftUI App Open View
struct AppOpenView: View {
@StateObject private var viewModel = AppOpenViewModel()
var body: some View {
ZStack {
Color.blue.ignoresSafeArea()
VStack(spacing: 20) {
if viewModel.showSplash {
// Splash Screen Content
VStack(spacing: 16) {
Text(viewModel.splashMessage)
.font(.headline)
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal)
if viewModel.isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.scaleEffect(1.5)
}
}
} else {
// Main App Content
VStack {
Text("Welcome to the app!")
.font(.largeTitle)
.foregroundColor(.white)
Text("Main app content goes here")
.font(.body)
.foregroundColor(.white.opacity(0.8))
}
}
}
}
.onAppear {
viewModel.initializeSDKAndLoadAd()
}
}
}
// MARK: - SwiftUI App Open ViewModel
class AppOpenViewModel: ObservableObject, AppOpenAdsDelegate {
@Published var showSplash = true
@Published var isLoading = true
@Published var splashMessage = "Watch an ad from our sponsor while the app loads..."
private let appKey = "<your-app-key>"
private let appOpenPlacementId = "<placement-id>"
func initializeSDKAndLoadAd() {
XMediatorAds.appOpen.addDelegate(self)
// Initialize SDK and load App Open ad
XMediatorAds.startWith(appKey: appKey) { result in
XMediatorAds.appOpen.load(placementId: self.appOpenPlacementId)
}
// Show ad after a short delay to ensure splash screen is visible
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showAppOpenAdIfReady()
}
}
private func showAppOpenAdIfReady() {
if XMediatorAds.appOpen.isReady(withPlacementId: appOpenPlacementId) {
// Get the root view controller to present the ad
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
XMediatorAds.appOpen.present(withPlacementId: appOpenPlacementId, fromViewController: rootViewController, fromAdSpace: "app-open-ad-space")
}
} else {
// No ad ready, proceed to main app
proceedToMainApp()
}
}
private func proceedToMainApp() {
DispatchQueue.main.async {
self.isLoading = false
self.showSplash = false
}
}
// MARK: - AppOpenAdsDelegate
func didLoad(placementId: String, result: LoadResult) {
print("App Open loaded! placementId: \(placementId)")
}
func didPresent(placementId: String) {
print("App Open is being presented! placementId: \(placementId)")
// Hide splash screen when ad is shown
DispatchQueue.main.async {
self.isLoading = false
self.showSplash = false
}
}
func failedToPresent(placementId: String, error: PresentError) {
print("App Open failed to present. placementId: \(placementId), Reason: \(error.localizedDescription)")
proceedToMainApp()
}
func didRecordImpression(placementId: String, data: ImpressionData) {
print("App Open impression, with revenue: \(data.revenue). PlacementId \(placementId)")
}
func willDismiss(placementId: String) {
print("App Open will be dismissed! placementId: \(placementId)")
}
func didDismiss(placementId: String) {
print("App Open dismissed! placementId: \(placementId)")
proceedToMainApp()
}
func didClick(placementId: String) {
print("App Open clicked! placementId: \(placementId)")
}
}
// MARK: - SwiftUI App Structure
struct AppOpenApp: App {
var body: some Scene {
WindowGroup {
AppOpenView()
}
}
}
AppOpenViewController.m
@import UIKit;
@import XMediatorObjC;
@interface AppOpenViewController ()<AppOpenAdsDelegate>
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *splashProgressBar;
@property (weak, nonatomic) IBOutlet UILabel *splashMessage;
@property (strong, nonatomic) NSString *appKey;
@property (strong, nonatomic) NSString *appOpenPlacementId;
@end
@implementation AppOpenViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.appKey = @"<your-app-key>";
self.appOpenPlacementId = @"<placement-id>";
[self setupSplashUI];
[self initializeSDKAndLoadAd];
}
- (void)setupSplashUI {
// Show user-friendly message about upcoming ad
self.splashMessage.text = @"Watch an ad from our sponsor while the app loads...";
[self.splashProgressBar startAnimating];
}
- (void)initializeSDKAndLoadAd {
[X3MXMediatorAds.appOpen addDelegate:self];
// Initialize SDK and load App Open ad
[X3MXMediatorAds startWithAppKey:self.appKey
callback:^(NSError * _Nullable error) {
[X3MXMediatorAds.appOpen loadWithPlacementId:self.appOpenPlacementId];
}];
// Show ad after a short delay to ensure splash screen is visible
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self showAppOpenAdIfReady];
});
}
- (void)showAppOpenAdIfReady {
if ([X3MXMediatorAds.appOpen isReadyWithPlacementId:self.appOpenPlacementId]) {
[X3MXMediatorAds.appOpen presentWithPlacementId:self.appOpenPlacementId fromViewController:self fromAdSpace:@"app-open-ad-space"];
} else {
// No ad ready, proceed to main app
[self proceedToMainApp];
}
}
- (void)proceedToMainApp {
// Hide splash elements and show main app content
[self.splashProgressBar stopAnimating];
self.splashProgressBar.hidden = YES;
self.splashMessage.text = @"Welcome to the app!";
// Navigate to main view controller or show main content
// [self performSegueWithIdentifier:@"showMainApp" sender:self];
}
#pragma mark - Ads Delegate
- (void)didLoadWithPlacementId:(NSString * _Nonnull)placementId result:(LoadResult * _Nonnull)result {
NSLog(@"App Open loaded! placementId: %@", placementId);
}
- (void)didPresentWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open is being presented! placementId: %@", placementId);
}
- (void)failedToPresentWithPlacementId:(NSString * _Nonnull)placementId error:(NSError * _Nonnull)error {
// If you need to resume your app's flow, make sure to do it here and in the didDismiss callback
NSLog(@"App Open failed to present. placementId: %@. Reason %@", placementId, error);
}
- (void)didRecordImpressionWithPlacementId:(NSString * _Nonnull)placementId data:(ImpressionData * _Nonnull)data {
NSLog(@"App Open impression, with revenue: %f. PlacementId %@", data.revenue, placementId);
}
- (void)willDismissWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open will be dismissed! placementId: %@", placementId);
}
- (void)didDismissWithPlacementId:(NSString * _Nonnull)placementId {
// If you need to resume your app's flow, make sure to do it here and in the failedToPresent callback
NSLog(@"App Open dismissed! placementId: %@", placementId);
}
- (void)didClickWithPlacementId:(NSString * _Nonnull)placementId {
NSLog(@"App Open clicked! placementId: %@", placementId);
}
@end
Advanced Use Cases
Need more control over the ad loading and presentation lifecycle? Check out the Advanced App Open API for fine-grained control using individual AppOpen
instances.